/*
Suppose that we create two UNIX domain datagram sockets bound to the paths
/somepath/a and /somepath/b , and that we connect the socket /somepath/a to /somepath/b .
What happens if we create a third datagram socket and try to send (sendto()) a
datagram via that socket to /somepath/a ? Write a program to determine the answer.
If you have access to other UNIX systems, test the program on those systems to see
if the answer differs.
*/

/*
response:
to connect or send datagrams write permission required
 */

#include <stdio.h>
#include <stdlib.h>
#include <sys/un.h>
#include <sys/socket.h>
#include <pthread.h>

#include "../common/logger.h"

#define SOCKET_SERVER "server_udp"
#define SOCKET_CLIENT_A "client_a"
#define SOCKET_CLIENT_B "client_b"

#define BUFFER_SIZE 100

struct mailman
{
  struct sockaddr_un from;
  struct sockaddr_un to;
};

static void *
client_thread_func(void *arg)
{
  int cfd = socket(AF_UNIX, SOCK_DGRAM, 0);
  if (cfd == -1)
    log_system_error("unable to create client socket");

  struct mailman *mailman = arg;
  if (bind(cfd, (struct sockaddr *)&mailman->from, sizeof(mailman->from))
      == -1) log_system_error("unable to bind client socket");

  sendto(cfd, "hello", 5, 0, (struct sockaddr *)&mailman->to,
         sizeof(mailman->to));
}

static void
initialize_address(struct sockaddr_un *addr, const char *name)
{
  memset(addr, 0, sizeof(*addr));
  addr->sun_family = AF_UNIX;
  strncpy(&addr->sun_path[1], name, sizeof(addr->sun_path) - 1);
}

static void
activate_client(pthread_t *thread, struct mailman *mailman)
{
  int s = pthread_create(thread, NULL, client_thread_func, mailman);
  if (s != 0)
    log_system_error("couldn't create thread");
}

int
main(void)
{
  struct sockaddr_un server_addr, clienta_addr, clientb_addr;
  initialize_address(&server_addr, SOCKET_SERVER);
  initialize_address(&clienta_addr, SOCKET_CLIENT_A);
  initialize_address(&clientb_addr, SOCKET_CLIENT_B);

  int sfd = socket(AF_UNIX, SOCK_DGRAM, 0);
  if (sfd == -1)
    log_system_error("unable to create server socket");

  if (bind(sfd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1)
    log_system_error("unable to bind socket");

  struct mailman clienta = {clienta_addr, server_addr};
  struct mailman clientb = {clientb_addr, clienta_addr};

  pthread_t clienta_thread;
  activate_client(&clienta_thread, &clienta);

  pthread_t clientb_thread;
  activate_client(&clientb_thread, &clientb);
  int bytes;
  char buffer[BUFFER_SIZE];
  for (;;)
    {
      bytes = recv(sfd, buffer, BUFFER_SIZE, 0);
      if (bytes == -1)
        log_system_error("recv error");
      printf("server response: %.*s\n", bytes, buffer);
    }

  void *result;
  pthread_join(clienta_thread, &result);
  pthread_join(clientb_thread, &result);
  return EXIT_SUCCESS;
}
